import requests
import pandas as pd
from enum import Enum
from typing import Optional
from loguru import logger
from datetime import datetime
from services.contract_info_service import ContractInfoService
from services.commons import Exchange
from config.settings import settings


class IndexFutures(Enum):
    """股指期货品种枚举

    key 为英文标识; value 为期货品种名称, 与新API的 symbol_type 参数保持一致
    """

    IM = "中证1000股指期货"  # CSI 1000
    IF = "沪深300指数期货"    # CSI 300  
    IC = "中证500指数期货"    # CSI 500
    IH = "上证50指数期货"     # SSE 50


class FuturesRealtimeService:
    """期货实时行情服务类"""

    def __init__(self):
        self.contract_info_service = ContractInfoService()
        self.base_url = settings.data_query_host

    def get_realtime_data(
        self,
        futures: IndexFutures,
        return_raw: bool = False,
    ) -> Optional[pd.DataFrame]:
        """获取指定股指期货的实时行情数据

        Args:
            futures: IndexFutures 枚举值, 选择需要查询的股指期货
            return_raw: 是否返回原始未处理的 DataFrame, 默认为 False, 返回处理后按 symbol 排序的 DataFrame

        Returns:
            - 默认返回处理后按 symbol 排序的 ``pd.DataFrame``，包含计算的涨跌额和涨跌幅
            - 当 ``return_raw`` 为 True 时, 返回原始的 ``pd.DataFrame``
            - 如果获取失败, 返回 ``None``
        """
        try:
            # 构建 API URL
            url = f"{self.base_url}/futures/realtime/symbol-type"
            
            # 请求参数
            params = {
                "symbol_type": futures.value
            }
            
            logger.info(f"Requesting futures realtime data from {url} with params: {params}")
            
            # 发送HTTP请求
            response = requests.get(
                url,
                params=params,
                timeout=settings.request_timeout_seconds,
            )
            response.raise_for_status()
            
            # 解析响应
            result = response.json()
            
            if result.get("code") != 200:
                logger.error(f"API returned error: {result.get('message', 'Unknown error')}")
                return None
            
            data = result.get("data", [])
            
            if not data:
                logger.warning(f"No data returned for {futures.value}")
                return None
            
            # 转换为 DataFrame
            df = pd.DataFrame(data)
            
            # 如果要求返回原始数据
            if return_raw:
                return df
            
            # 过滤掉连续合约, 连续合约以单独的 0 结尾 (如 IC0, IF0 等)
            # 但保留真实合约如 IM2510 (年月为2510，即2025年10月)
            df = df[~df["symbol"].astype(str).str.match(r'^[A-Z]{2}0$')].reset_index(drop=True)
            
            # 按 symbol 排序
            df = df.sort_values(by="symbol").reset_index(drop=True)

            # API 已经返回了 changepercent 字段，但为了保持兼容性，我们也计算 change 字段
            df['changepercent'] = df['trade']/df['preclose'] - 1
            df['change'] = df['trade'] - df['preclose']

            # 获取合约到期日信息并补充到 DataFrame
            df = self._add_contract_expiration_info(df)

            return df

        except Exception as e:
            logger.error(f"Failed to get realtime futures data for {futures.value}: {e}")
            return None
    
    def _add_contract_expiration_info(self, df: pd.DataFrame) -> pd.DataFrame:
        """添加合约到期日信息和计算距离到期日的天数
        
        Args:
            df: 包含股指期货实时数据的 DataFrame
            
        Returns:
            添加了到期日信息的 DataFrame
        """
        try:
            # 获取合约代码列表
            symbols = df['symbol'].tolist()
            
            # 从 ContractInfoService 获取合约信息 (股指期货在中金所 CFFEX)
            contract_info_df = self.contract_info_service.get_contract_list_date_and_last_date(
                exchange=Exchange.CFFEX, 
                symbols=symbols
            )
            
            if contract_info_df.empty:
                logger.warning("未能获取到合约到期日信息")
                # 添加空的到期日列
                df['expiry_date'] = None
                df['days_to_expiry'] = None
                return df
            
            # 将合约信息合并到主 DataFrame
            merged_df = df.merge(
                contract_info_df[['symbol', 'last_trading_date']], 
                left_on='symbol', 
                right_on='symbol', 
                how='left'
            )
            
            # 重命名列
            merged_df['expiry_date'] = merged_df['last_trading_date']
            
            # 计算距离到期日的天数
            current_date = datetime.now().date()
            merged_df['days_to_expiry'] = merged_df['expiry_date'].apply(
                lambda x: self._calculate_days_to_expiry(x, current_date) if pd.notna(x) else None
            )
            
            # 删除临时列
            merged_df = merged_df.drop(columns=['last_trading_date'])
            
            return merged_df
            
        except Exception as e:
            logger.error(f"添加合约到期日信息时出错: {e}")
            # 如果出错，返回原始 DataFrame 并添加空的到期日列
            df['expiry_date'] = None
            df['days_to_expiry'] = None
            return df
    
    def _calculate_days_to_expiry(self, expiry_date_str: str, current_date) -> Optional[int]:
        """计算距离到期日的天数
        
        Args:
            expiry_date_str: 到期日字符串 (格式可能是 YYYYMMDD)
            current_date: 当前日期
            
        Returns:
            距离到期日的天数，如果计算失败返回 None
        """
        try:
            # 尝试解析日期字符串 (假设格式为 YYYYMMDD)
            if isinstance(expiry_date_str, str) and len(expiry_date_str) == 8:
                expiry_date = datetime.strptime(expiry_date_str, "%Y%m%d").date()
            else:
                # 如果已经是日期对象
                expiry_date = pd.to_datetime(expiry_date_str).date()
            
            # 计算天数差
            days_diff = (expiry_date - current_date).days
            return days_diff
            
        except Exception as e:
            logger.warning(f"解析到期日期失败: {expiry_date_str}, 错误: {e}")
            return None 
